#include "DXUT.h"

#include "MainHeader.h"

#include "Loader.h"
#include "Updater.h"
#include "Helper.h"

#include <vector>

using namespace std;

#define lerp(t, a, b) (a + t * (b - a))

void CALLBACK bOnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );

inline float sgn(float x) {
	return ((x > 0.0f) ? 1.0f : ((x < 0.0f) ? -1.0f : 0.0f));
}

inline Region * World::getRegion(int x, int z) {
	return map->getRegion((x >> 4), (z >> 4));
}

inline Region * World::createRegion(int x, int z) {
	return map->createRegion(this, m_Seed, (x >> 4), (z >> 4));
}

inline  Region * World::removeRegion(int x, int z) {
	return map->removeRegion((x >> 4), (z >> 4));
}

EntityWorld::~EntityWorld() {

	while( !m_pEntityItems.empty() ) {
		EntityItem * ent = m_pEntityItems.back();
		m_pEntityItems.pop_back();
		delete ent;
	}

	while( !m_pEntityLivings.empty() ) {
		EntityLiving * ent = m_pEntityLivings.back();
		m_pEntityLivings.pop_back();
		delete ent;
	}

	while( !m_pPlayers.empty() ) {
		Player * ent = m_pPlayers.back();
		m_pPlayers.pop_back();
		delete ent;
	}

}

vector<EntityItem*> * EntityWorld::getAllItemsInRange(D3DXVECTOR3 position, float distance) {
	vector<EntityItem*> * items = new vector<EntityItem*>();

	for (auto it = m_pEntityItems.begin(); it != m_pEntityItems.end(); it++) {
		EntityItem * ent = (*it);
		D3DXVECTOR3 dist = ent->getPosition() - position;
		if (D3DXVec3Length(&dist) <= distance) {
			items->push_back(ent);
		}
	}

	return items;
}

void EntityWorld::FrameMove( double fTime, float fElapsedTime) {
	for (auto it = m_pEntityItems.begin(); it != m_pEntityItems.end(); it++) {
		(*it)->OnFrameMove( this, fTime, fElapsedTime);
	}

	for (auto it = m_pEntityLivings.begin(); it != m_pEntityLivings.end(); it++) {
		(*it)->OnFrameMove( this, fTime, fElapsedTime);
	}

	for (auto it = m_pPlayers.begin(); it != m_pPlayers.end(); it++) {
		(*it)->OnFrameMove( this, fTime, fElapsedTime);
	}
}

int World::loadRegion(int x, int z) {
	BYTE ix = abs(x - ((x >> 4) << 4));
	BYTE iz = abs(z - ((z >> 4) << 4));

	Region * regio = createRegion(x, z);
	if (!regio) {
		printf("Failed to create Region!\n");
		return -1;
	}
	auto it = setcalls.begin();
	SetCallBuffer buff = {x, z, 0, 0, 0, 0, 0};
	for (;;) {
		it = find(it, setcalls.end(), buff);
		if (it != setcalls.end()) {
			if (buff == *it) {
				buff = *it;
				regio->setBlock(buff.ix, buff.iz, buff.iy, buff.block, buff.flags);
				setcalls.erase(it);
			}
		} else {
			break;
		}
	}
	return map->loadChunk((x >> 4), (z >> 4), ix, iz);
}

Region * World::unloadRegion(int x, int z, Chunk ** chunks) {
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));
	return map->unloadChunk((x >> 4), (z >> 4), ix, iz, chunks);
}

void World::generateStructures(int x, int z) {
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));

	Region * regio = getRegion(x, z);
	if (regio) {
		byte height[256];
		regio->generateStructures(ix, iz, height);
	}
}

void World::lightPass() {
	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		if (regio) {
			regio->lightPass();
		}
	}
	for (int i = map->size() - 1; i >= 0; i--) {
		Region * regio = map->getValue(i);
		if (regio) {
			regio->lightPass2();
		}
	}
}

void World::lightPass(int x, int z) {
	// Chunkcoord -> Regioncoord
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));

	Region * regio = getRegion(x, z);
	if (regio) {
		regio->lightPass2(ix, iz);
		regio->lightPass(ix, iz);
	} 
}

void World::resetLight() {
	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		if (regio) {
			regio->resetLight();
		}
	}
}

void World::resetLight(int x, int z) {
	// Chunkcoord -> Regioncoord
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));

	Region * regio = getRegion(x, z);
	if (regio) {
		regio->resetLight(ix, iz);
	} 
}

void World::update() {
	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		if (regio) {
			regio->update();
		}
	}
}

byte World::updateBlock(int x, int z, int y, byte type, byte * ticks) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->updateBlock(x, z, y, type, ticks);
	} else {
		return 0xFF;
	}
}

void World::delayedUpdateBlock(int x, int z, int y, byte type, byte ticks)  {
	QueElement elem = {ticks, INTVECTOR4(x,z,y,type)};
	m_pTickUpdater->setValue(&elem);
}

World::~World() {
	ResumeThread( m_pTickUpdater->m_Thread_Handle );
	m_pTickUpdater->Terminate();

	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		if (regio)
			SAFE_DELETE( regio );
	}

	delete map;
}

World::World() {
	Dirty				= false;

	m_Daytime	= 0.0f;
	m_Daylength = 50.0f;

	map				= new Regionmap();
	m_pTickUpdater	= new TickUpdater(this);
}

float inline smoothstep(float edge0, float edge1, float x) 
{
	x = max(0.0f, min(1.0f, ((x - edge0)/(edge1 - edge0))));
	return x*x*x*(x*(x*6 - 15) + 10);
}

void World::FrameMove( double fTime, float fElapsedTime) {
	m_Daytime += fElapsedTime * 0.3f;

	EntityWorld::FrameMove( fTime, fElapsedTime );
}

Block World::getBlock(int x, int z, int y) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->getBlock(x, z, y);
	} else {
		return VOID_BLOCK;
	}
}

Block * World::getpBlock(int x, int z, int y) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->getpBlock(x, z, y);
	} else {
		return NULL;
	}
}


Block World::breakBlock(int x, int z, int y) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		delayedUpdateBlock(x, z, y, BLOCK_REMOVED, 0);
		Block block = regio->breakBlock(x, z, y);
		delayedUpdateBlock(x, z, y, BLOCK_PLACED, 0);
		renewDirty(true);
		return block;
	} else {
		return VOID_BLOCK;
	}
}

int World::placeBlock(int x, int z, int y, Block block) {
	setBlock(x, z, y, block);
	delayedUpdateBlock(x, z, y, BLOCK_PLACED, 0);
	renewDirty(true);
	return 1;
}

void World::setBlock(int x, int z, int y, Block block, byte flags) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->setBlock(x, z, y, block, flags);
	}	else if ((flags & UPDATE_DELAY) == UPDATE_DELAY){
		SetCallBuffer buff = {static_cast<byte>(x >> 4), static_cast<byte>(z >> 4), static_cast<byte>(y), x, z, y, block, flags};
		setcalls.push_back(buff);
	}
}

float World::getTemperature(int x, int z) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->getTemperature(x,z);
	}
	return 0.0f;
}

float World::getHumidity(int x, int z) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->getHumidity(x,z);
	}
	return 0.0f;
}

BYTE World::getBiome(int x, int z) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->getBiome(x,z);
	}
	return 0;
}

D3DXVECTOR3 World::getPlayerPosition(int index) {
	return D3DXVECTOR3(0.0f, 0.0f, 0.0f);
};

#ifdef RENDER

RenderableWorld::RenderableWorld(CFirstPersonCamera * cam, HUDDialogResourceManager* manager)/* : World()*/ {
	Dirty				= false;

	m_Daytime			= 0.0f;
	m_Daylength			= 50.0f;

	m_pd3dDevice		= NULL;
	m_pDebugText		= NULL;
#ifdef DEFERRED_UPDATE
	InitializeSRWLock(&DEFERRED_LOCK);
#endif

	m_bLeftButtonDown	= false;
	m_bRightButtonDown  = false;
	m_bMiddleButtonDown = false;
	m_bSideButton1Down	= false;
	m_bSideButton2Down	= false;

	m_bWKey = false;
	m_bAKey = false;
	m_bSKey = false;
	m_bDKey = false;
	m_bJump = false;
	m_bAlt	 = false;
	m_bShift = false;

	m_freefly			= true;
	m_showDebug			= true;
	m_SafeFiles			= true;

	/* DEBUG */
	printf ("Beginning generation ...\n");

	m_Camera	= cam;
	m_Camera->SetScalers(0.01f, 50.0f);

	m_EyePt		= *m_Camera->GetEyePt();

	/* Load Savefile*/
	CreateDirectory(L"Savegames", NULL); 

	int file_exists;
	char filename[] = "Savegames/Savefile.sv";

	fopen_s(&m_file, filename,"r");
	if (m_file == NULL) 
		file_exists = 0;
	else {
		file_exists = 1; 
		fclose(m_file);
	}

	if (file_exists == 1)
	{
		printf("%s opened!\n", filename);
		fopen_s(&m_file, filename,"r+b");
		fread(&m_EyePt, sizeof(D3DXVECTOR3), 1, m_file);
		D3DXVECTOR3 lookat = *cam->GetLookAtPt();
		cam->SetViewParams(&m_EyePt, &lookat);
		fread(&m_Daytime, sizeof(double), 1, m_file);
		fread(&m_Seed, sizeof(int), 1, m_file);
	}
	else
	{
		printf("%s created!\n", filename);
		fopen_s(&m_file, filename,"w+b");
		fwrite(&m_EyePt, sizeof(D3DXVECTOR3), 1, m_file);
		fwrite(&m_Daytime, sizeof(double), 1, m_file);

		srand(time(NULL));
		m_Seed = rand();
		fwrite(&m_Seed, sizeof(int), 1, m_file);
	}

	/* Load Region */
	map				= new Regionmap();

	/* Threads last */
	m_pLoader		= new Loader(this);
	m_pTickUpdater	= new TickUpdater(this);

	/*  Geometry */
	m_Sky		= new Skybox();

	/* UI */
	m_DialogResourceManager = manager;
	m_Inventory = new Inventory(10, 10, m_DialogResourceManager);

	m_pPlayer = new Player(m_EyePt);
	m_LifeHUD.Init( m_DialogResourceManager );
	m_LifeHUD.AddLifebar( m_pPlayer, 4, 3, 5, 500, 20);
	m_InventoryHUD.Init( m_DialogResourceManager );
	m_InventoryHUD.AddInventory( m_Inventory, 5, 300, 720);
//	m_LifeHUD.SetCallback( bOnGUIEvent );

	m_pSound	= new SoundEngine();

	m_pSound->InitDirectSound(DXUTGetHWND());
	m_pSound->OpenOgg(AMBIENT_SOUND_1);
	m_pSound->Play(true);
}

RenderableWorld::~RenderableWorld() {
	ResumeThread( m_pLoader->m_Thread_Handle );
	m_pLoader->Terminate();

	OnD3D11DestroyDevice( NULL );
	delete m_Sky;
	delete m_Inventory;

	delete m_pPlayer;

	m_pSound->Close();
	delete m_pSound;

	if (m_SafeFiles) {
		if (m_file) {
			fseek(m_file, 0, SEEK_SET);
			fwrite(&m_EyePt, sizeof(D3DXVECTOR3), 1, m_file);
			fwrite(&m_Daytime, sizeof(double), 1, m_file);
			fclose(m_file);
		}

		m_file = NULL;
	}

}

void RenderableWorld::FrameMove( double fTime, float fElapsedTime) {
	World::FrameMove(fTime, fElapsedTime);
	m_EyePt = *m_Camera->GetEyePt();

	D3DXVECTOR3 day			= D3DXVECTOR3(1.0f, 1.0f, 0.9f);
	D3DXVECTOR3 darkNight	= D3DXVECTOR3(0.005f, 0.005f, 0.010f);
	D3DXVECTOR3 brightNight	= D3DXVECTOR3(0.030f, 0.030f, 0.080f);
	D3DXVECTOR3 night;

	float moonlight = 0.0f;
	float sunlight	= m_Sky->setTime((m_Daytime / m_Daylength), &moonlight);

	if (sunlight < 0.3f) {
		m_Daytime += fElapsedTime * 10.0f;
	}

	/* Skylight */
	D3DXVec3Lerp(&night, &darkNight, &brightNight, moonlight);
	D3DXVec3Lerp(&m_Skylight, &night, &day, sunlight);
	/* Skylight */

	/* Backgroundcolor */
	day     = D3DXVECTOR3(0.3f, 0.3f, 0.4f);
	night   = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	D3DXVec3Lerp(&m_Skycolor, &night, &day, sunlight);
	/* Backgroundcolor */

	int bx = static_cast<int>(floor(m_EyePt.x + 0.5f)); 
	int by = static_cast<int>(floor(m_EyePt.y + 0.5f)); 
	int bz = static_cast<int>(floor(m_EyePt.z + 0.5f));
	m_bid = getBlock(bx, bz, by).blockID;
	if (m_bid == WATER) {
		m_fogEnd	= 50.0f;
		m_fogStart	= 5.0f;
		m_fogColor	= D3DXVECTOR3(0.1f, 0.1f, 1.0f);
	} else if (m_bid == LAVA) {
		m_fogEnd	= 5.0f;
		m_fogStart	= 0.0f;
		m_fogColor	= D3DXVECTOR3(1.5f, 0.4f, 0.2f);
	} else {
		m_fogEnd	= lerp(sqrt(sunlight), lerp(sqrt(moonlight), 150.0f, 250.0f), 280.0f);
		m_fogStart	= lerp(sqrt(sunlight), lerp(sqrt(moonlight), 100.0f, 180.0f), 250.0f);
		m_fogColor	= m_Skycolor;
	}

	if (!m_freefly) {
		m_pPlayer->Movement(m_Camera->getRotation(), m_bShift, m_bJump, m_bWKey, m_bSKey, m_bAKey, m_bDKey, fElapsedTime);
		m_pPlayer->OnFrameMove( this, fTime, fElapsedTime);

		/* HANDLE ITEMS */
		vector<EntityItem*> * items = getAllItemsInRange(m_pPlayer->getPosition(), 1.50f);
		while( !items->empty() ) {
			EntityItem * ent	= items->back();
			Item * item			= ent->getItem();
			items->pop_back();

			DWORD id = item->getID();
			int i = 0;
			for (; i < 10; i++) {
				Item * bufferitem = m_Inventory->getItemAtIndex(i);
				if (bufferitem)
					if (bufferitem->getID() == id) {
						bufferitem->setQuantity(item->getQuantity() + bufferitem->getQuantity());
						break;
					}
			}
			if (i == 10) {
				i = 0;
				for (; i < 10; i++) {
					Item * bufferitem = m_Inventory->getItemAtIndex(i);
					if (bufferitem == NULL) {
						m_Inventory->setItem(item->clone(), i);
						break;
					}
				}
			}

			for (auto it = m_pEntityItems.begin(); it != m_pEntityItems.end(); it++) {
				if ((*it) == ent) {
					ent->OnD3D11DestroyDevice();
					m_pEntityItems.erase(it);
					m_pSound->Quickplay(POP_SOUND);
					break;
				}
			}
		}
		delete items;
		/* HANDLE ITEMS */

		D3DXVECTOR3 diff = m_pPlayer->getEyePoint() - m_EyePt; 
		m_Camera->MoveCamera( &diff );
	} else {
		m_pPlayer->setPosition(m_EyePt);
	}

	m_pSound->OnFrameMove(fTime, fElapsedTime);
}

void RenderableWorld::generateTargetBox(ID3D11Device* pd3dDevice) {
	HRESULT hr = S_OK;

	ID3DBlob* pVSBlob = NULL;
	hr = CompileShaderFromFile( L"BasicShader.fx", "VS", "vs_4_0", &pVSBlob );
	if( FAILED( hr ) )
	{
		MessageBox( NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
		return;
	}

	// Create the vertex shader
	hr = pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pVertexShader );
	if( FAILED( hr ) )
	{	
		SAFE_RELEASE(pVSBlob);
		return;
	}

	// Define the input layout
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
	UINT numElements = ARRAYSIZE( layout );

	// Create the input layout
	hr = pd3dDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
		pVSBlob->GetBufferSize(), &m_pVertexLayout );
	pVSBlob->Release();
	if( FAILED( hr ) )
		return;

	// Compile the pixel shader
	ID3DBlob* pPSBlob = NULL;
	hr = CompileShaderFromFile( L"BasicShader.fx", "PS", "ps_4_0", &pPSBlob );
	if( FAILED( hr ) )
	{
		MessageBox( NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
		return;
	}

	// Create the pixel shader
	hr = pd3dDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &m_pPixelShader );
	pPSBlob->Release();
	if( FAILED( hr ) )
		return;

	BasicVertex vertices[] =
	{
		{ D3DXVECTOR3( -0.501f, 0.501f, -0.501f )}, 
		{ D3DXVECTOR3( 0.501f, 0.501f, -0.501f )},
		{ D3DXVECTOR3( 0.501f, 0.501f, 0.501f )},
		{ D3DXVECTOR3( -0.501f, 0.501f, 0.501f )},

		{ D3DXVECTOR3( -0.501f, -0.501f, -0.501f )},
		{ D3DXVECTOR3( 0.501f, -0.501f, -0.501f )},
		{ D3DXVECTOR3( 0.501f, -0.501f, 0.501f )},
		{ D3DXVECTOR3( -0.501f, -0.501f, 0.501f )},
	};
	D3D11_BUFFER_DESC bd;
	ZeroMemory( &bd, sizeof(bd) );
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( BasicVertex ) * 8;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory( &InitData, sizeof(InitData) );
	InitData.pSysMem = vertices;
	pd3dDevice->CreateBuffer( &bd, &InitData, &m_pVertexBuffer );

	// Create index buffer
	// Create vertex buffer
	WORD indices[] =
	{
		0,1,2,3,0,4,5,6,7,4,7,3,2,6,5,1
	};
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.ByteWidth = sizeof( WORD ) * 16;        // 36 vertices needed for 12 triangles in a triangle list
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.CPUAccessFlags = 0;
	InitData.pSysMem = indices;
	pd3dDevice->CreateBuffer( &bd, &InitData, &m_pIndexBuffer );

	indexSize = 16;

	/* Constant Buffer */
	D3D11_BUFFER_DESC cbDesc;
	ZeroMemory( &cbDesc, sizeof(cbDesc) );

	cbDesc.Usage			= D3D11_USAGE_DEFAULT;
	cbDesc.BindFlags		= D3D11_BIND_CONSTANT_BUFFER;
	cbDesc.CPUAccessFlags	= 0;
	cbDesc.ByteWidth		= sizeof( CB_VS_PER_FRAME );
	pd3dDevice->CreateBuffer( &cbDesc, NULL, &m_pcbVSPerFrame11 );

	cbDesc.Usage			= D3D11_USAGE_DEFAULT;
	cbDesc.BindFlags		= D3D11_BIND_CONSTANT_BUFFER;
	cbDesc.CPUAccessFlags	= 0;
	cbDesc.ByteWidth		= sizeof( CB_PS_PER_FRAME );
	pd3dDevice->CreateBuffer( &cbDesc, NULL, &m_pcbPSPerFrame11 );

	cbDesc.Usage			= D3D11_USAGE_DYNAMIC;
	cbDesc.BindFlags		= D3D11_BIND_CONSTANT_BUFFER;
	cbDesc.CPUAccessFlags	= D3D10_CPU_ACCESS_WRITE;
	cbDesc.ByteWidth		= sizeof( CB_VS_PER_OBJECT );
	pd3dDevice->CreateBuffer( &cbDesc, NULL, &m_pcbVSPerObject11 );
	/* Constant Buffer */

	/* Blend States */
	D3D11_BLEND_DESC BDesc;
	ZeroMemory( &BDesc, sizeof( D3D11_BLEND_DESC ) );
	BDesc.RenderTarget[0].BlendEnable		= TRUE;
	BDesc.RenderTarget[0].SrcBlend			= D3D11_BLEND_SRC_ALPHA;
	BDesc.RenderTarget[0].DestBlend			= D3D11_BLEND_INV_SRC_ALPHA;
	BDesc.RenderTarget[0].BlendOp			= D3D11_BLEND_OP_ADD;
	BDesc.RenderTarget[0].SrcBlendAlpha		= D3D11_BLEND_ONE;
	BDesc.RenderTarget[0].DestBlendAlpha	= D3D11_BLEND_ZERO;
	BDesc.RenderTarget[0].BlendOpAlpha		= D3D11_BLEND_OP_ADD;
	BDesc.RenderTarget[0].RenderTargetWriteMask = 0x0f;
	pd3dDevice->CreateBlendState( &BDesc, &m_pBlendState11 );
	/* Blend States */

	/* Sampler States */
	D3D11_SAMPLER_DESC SSDesc;
	ZeroMemory( &SSDesc, sizeof( D3D11_SAMPLER_DESC ) );
	SSDesc.Filter			= D3D11_FILTER_MIN_MAG_MIP_POINT;
	SSDesc.AddressU			= D3D11_TEXTURE_ADDRESS_CLAMP;
	SSDesc.AddressV			= D3D11_TEXTURE_ADDRESS_CLAMP;
	SSDesc.AddressW			= D3D11_TEXTURE_ADDRESS_CLAMP;
	SSDesc.ComparisonFunc	= D3D11_COMPARISON_NEVER;
	SSDesc.MaxAnisotropy	= 0;
	SSDesc.MinLOD			= 0;
	SSDesc.MaxLOD			= 2;//D3D11_FLOAT32_MAX;
	if ( pd3dDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_9_3 ) {
		SSDesc.Filter		= D3D11_FILTER_MIN_MAG_MIP_POINT;
		SSDesc.MaxAnisotropy = 0;
	}
	pd3dDevice->CreateSamplerState( &SSDesc, &m_pSamplerStateBlock11 );
	/* Sampler States */
}

int RenderableWorld::Release() {
	if ( m_pd3dDevice == NULL ) return -1;

	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		SAFE_RELEASE( regio );
	}
}

int RenderableWorld::Release(int x, int z) {
	if ( m_pd3dDevice == NULL ) return -1;

	// Chunkcoord -> Regioncoord
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));

	Region * regio = getRegion(x, z);
	if (regio) {
		regio->Release(ix, iz);
	} 

	return 0;
}

int RenderableWorld::generateVB(int x, int z) {
	if ( m_pd3dDevice == NULL ) return -1;

	// Chunkcoord -> Regioncoord
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));

	Region * regio = getRegion(x, z);
	if (regio) {
		if (m_pDeferredContext)
			regio->generateVB(m_pd3dDevice, m_pDeferredContext, ix, iz);
	} 
	return 0;
}

int RenderableWorld::generateVB() {
	if ( m_pd3dDevice == NULL ) return -1;
	if (Dirty) {
		if (m_pDeferredContext)
			for (int i = 0; i < map->size(); i++) {
				Region * regio = map->getValue(i);
				if (regio) {
					regio->generateVB(m_pd3dDevice, m_pDeferredContext);
				}
			}
	}
	return 0;
}


void RenderableWorld::setDirty(int x, int z, int y, bool immediatly) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		return regio->setDirty(x, z, y, immediatly);
	}
}

int RenderableWorld::renewDirty(bool immediatly) {
	if ( m_pd3dDevice == NULL ) return -1;
	if (m_pDeferredContext)
		for (int i = 0; i < map->size(); i++) {
			Region * regio = map->getValue(i);
			if (regio) {
				regio->renewDirty(m_pd3dDevice, m_pDeferredContext, immediatly);
			}
		}
		return 0;
}

void RenderableWorld::OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, double fTime, float fElapsedTime, void* pUserContext ) {
	float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
	if ((m_bid == LAVA) || (m_bid == WATER)) {
		ClearColor[0] = m_fogColor.x;
		ClearColor[1] = m_fogColor.y;
		ClearColor[2] = m_fogColor.z;
		ClearColor[3] = 1.0f;
	}
	ID3D11RenderTargetView* pRTV = DXUTGetD3D11RenderTargetView();
	pd3dImmediateContext->ClearRenderTargetView( pRTV, ClearColor );

	// Clear the depth stencil
	ID3D11DepthStencilView* pDSV = DXUTGetD3D11DepthStencilView();
	pd3dImmediateContext->ClearDepthStencilView( pDSV, D3D11_CLEAR_DEPTH, 1.0, 0 );

	if ((m_bid != LAVA) && (m_bid != WATER))
		m_Sky->D3D11Render(m_Camera, pd3dImmediateContext);

#ifdef DEFERRED_UPDATE
	ID3D11CommandList* pd3dCommandList = NULL;
	HRESULT hr;
	AcquireSRWLockExclusive(&DEFERRED_LOCK);
	hr = m_pDeferredContext->FinishCommandList(FALSE, &pd3dCommandList);
	ReleaseSRWLockExclusive(&DEFERRED_LOCK);
	if(pd3dCommandList)
	{
		pd3dImmediateContext->ExecuteCommandList(pd3dCommandList, TRUE);
		SAFE_RELEASE( pd3dCommandList );
	}
#endif

	D3DXMATRIX mWorld	= *m_Camera->GetWorldMatrix();
	D3DXMATRIX mView	= *m_Camera->GetViewMatrix();
	D3DXMATRIX mProj	= *m_Camera->GetProjMatrix();
	D3DXMATRIX mWorldViewProjection = mView * mProj;
	D3DXMatrixIdentity(&mWorld);
	D3DXVECTOR3 dir		= D3DXVECTOR3(1.0f, 0.0f, 0.0f);
	D3DXVec3Subtract(&dir, m_Camera->GetLookAtPt(), m_Camera->GetEyePt());
	D3DXVec3Normalize(&dir,&dir);

	m_EyePt				= *m_Camera->GetEyePt();
	m_Direction			= dir;

	CB_VS_PER_FRAME cb0;
	D3DXMatrixTranspose( &cb0.mView, &mView );
	D3DXMatrixTranspose( &cb0.mViewProjection, &mWorldViewProjection );
	cb0.cLightDiffuse	= D3DXVECTOR4( m_Skylight.x, m_Skylight.y, m_Skylight.z, 1.0f );
	cb0.vEyePos			= D3DXVECTOR4( m_EyePt.x, m_EyePt.y, m_EyePt.z, 1.0f );
	cb0.fFogEnd			= m_fogEnd;
	cb0.fFogStart		= m_fogStart;
	cb0.fTime			= (float)fTime / 50.0f;

	CB_PS_PER_FRAME cb1;
	cb1.cFogColor		= D3DXVECTOR4( m_fogColor.x, m_fogColor.y, m_fogColor.z, 1.0f );

	CB_VS_PER_OBJECT			cb2;
	D3D11_MAPPED_SUBRESOURCE	cbMapped;
	D3DXMatrixTranspose( &cb2.mWorld, &mWorld );

	if (pd3dImmediateContext->Map(m_pcbVSPerObject11, 0, D3D11_MAP_WRITE_DISCARD, 0, &cbMapped) == S_OK) {
		CopyMemory(cbMapped.pData, &cb2, sizeof( CB_VS_PER_OBJECT ));
		pd3dImmediateContext->Unmap(m_pcbVSPerObject11, 0);
	}

	pd3dImmediateContext->UpdateSubresource( m_pcbVSPerFrame11, 0, NULL, &cb0, 0, 0 );
	pd3dImmediateContext->UpdateSubresource( m_pcbPSPerFrame11, 0, NULL, &cb1, 0, 0 );

	pd3dImmediateContext->VSSetShader( m_pBlockVertexShader, NULL, 0 );
	pd3dImmediateContext->VSSetConstantBuffers( 0, 1, &m_pcbVSPerFrame11 );
	pd3dImmediateContext->VSSetConstantBuffers( 1, 1, &m_pcbVSPerObject11 );

	pd3dImmediateContext->PSSetShader( m_pBlockPixelShader, NULL, 0 );
	pd3dImmediateContext->PSSetConstantBuffers( 0, 1, &m_pcbPSPerFrame11 );
	pd3dImmediateContext->PSSetSamplers( 0, 1, &m_pSamplerStateBlock11 );
	pd3dImmediateContext->PSSetShaderResources( 0, 1, &m_pTextureRV );
	pd3dImmediateContext->PSSetShaderResources( 1, 1, &m_pTextureAlphaRV );

	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	pd3dImmediateContext->IASetInputLayout( m_pLayout11 );

	D3DXFRUSTUM frustum = buildViewFrustum(m_Camera);

	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		if (regio) {
			regio->render(frustum, pd3dImmediateContext, 0);
		}
	}

	/* Entities */
	Entity::InitDevice( m_pcbVSPerObject11 );
	pd3dImmediateContext->VSSetShader( m_pEntityVertexShader, NULL, 0 );
	for (auto it = m_pEntityItems.begin(); it != m_pEntityItems.end(); it++) {
		(*it)->OnD3D11FrameRender( frustum, this, pd3dImmediateContext, fTime, fElapsedTime);
	}

	for (auto it = m_pEntityLivings.begin(); it != m_pEntityLivings.end(); ) {
		EntityLiving * ent = (*it);
		ent->OnD3D11FrameRender( frustum, this, pd3dImmediateContext, fTime, fElapsedTime);
		if(!ent->isLiving()) {
			it = m_pEntityLivings.erase(it);
			ent->OnD3D11DestroyDevice();
			delete ent;
		} else {
			++it;
		}
	}


	float BlendFactor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
	pd3dImmediateContext->OMSetBlendState(m_pBlendState11, BlendFactor, 0xffffffff);

	pd3dImmediateContext->VSSetShader( m_pBlockAlphaVertexShader, NULL, 0 );
	pd3dImmediateContext->PSSetShader( m_pBlockAlphaPixelShader, NULL, 0 );

	if (pd3dImmediateContext->Map(m_pcbVSPerObject11, 0, D3D11_MAP_WRITE_DISCARD, 0, &cbMapped) == S_OK) {
		CopyMemory(cbMapped.pData, &cb2, sizeof( CB_VS_PER_OBJECT ));
		pd3dImmediateContext->Unmap(m_pcbVSPerObject11, 0);
	}

	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		if (regio) {
			regio->render(frustum, pd3dImmediateContext, 1);
		}
	}

	pd3dImmediateContext->OMSetBlendState(NULL, BlendFactor, 0xffffffff);
	pd3dImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP );

	D3DXVECTOR3 vec;
	Block block;
	if (ray(&m_EyePt, &dir, &vec, &block))  {
		D3DXMatrixTranslation(&mWorld, vec.x, vec.y, vec.z);		
		D3DXMatrixTranspose( &cb2.mWorld, &mWorld );

		if (pd3dImmediateContext->Map(m_pcbVSPerObject11, 0, D3D11_MAP_WRITE_DISCARD, 0, &cbMapped) == S_OK) {
			CopyMemory(cbMapped.pData, &cb2, sizeof( CB_VS_PER_OBJECT ));
			pd3dImmediateContext->Unmap(m_pcbVSPerObject11, 0);
		}
		//
		// Render the cube
		//
		pd3dImmediateContext->VSSetShader( m_pVertexShader, NULL, 0 );
	//	pd3dImmediateContext->VSSetConstantBuffers( 1, 1, &m_pcbVSPerObject11 );
		pd3dImmediateContext->PSSetShader( m_pPixelShader, NULL, 0 );

		UINT stride = sizeof( BasicVertex );
		UINT offset = 0;

		// Set the input layout
		pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );

		pd3dImmediateContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );
		pd3dImmediateContext->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );

		pd3dImmediateContext->DrawIndexed( indexSize, 0, 0 );
	}

	m_Inventory->OnRender( fElapsedTime );

	m_LifeHUD.OnRender( fElapsedTime );
	m_InventoryHUD.OnRender( fElapsedTime );

	if ( m_showDebug )
		drawDebugText();
}

void RenderableWorld::OnD3D11CreateDevice( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ) {

	generateTargetBox(pd3dDevice);

	m_Sky->OnD3D11CreateDevice(pd3dDevice);

	for (auto it = m_pEntityItems.begin(); it != m_pEntityItems.end(); it++) {
		(*it)->OnD3D11CreateDevice( pd3dDevice );
	}
	for (auto it = m_pEntityLivings.begin(); it != m_pEntityLivings.end(); it++) {
		(*it)->OnD3D11CreateDevice( pd3dDevice );
	}

	D3DX11CreateShaderResourceViewFromFile( pd3dDevice, L"Textures/terrain.png", NULL, NULL, &m_pTextureRV, NULL );
	D3DX11CreateShaderResourceViewFromFile( pd3dDevice, L"Textures/terrain_c.png", NULL, NULL, &m_pTextureAlphaRV, NULL );

	HRESULT hr = S_OK;

	ID3DBlob* pVSBlob = NULL;
	hr = CompileShaderFromFile( L"SimpleSample.fx", "RenderSceneVS", "vs_4_0", &pVSBlob );
	if( FAILED( hr ) )
	{
		MessageBox( NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
		return;
	}

	// Create the vertex shader
	hr = pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pBlockVertexShader );
	if( FAILED( hr ) )
	{	
		SAFE_RELEASE(pVSBlob);
		return;
	}


	hr = CompileShaderFromFile( L"SimpleSample.fx", "RenderSceneEntityVS", "vs_4_0", &pVSBlob );
	if( FAILED( hr ) )
	{
		MessageBox( NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
		return;
	}

	// Create the vertex shader
	hr = pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pEntityVertexShader );
	if( FAILED( hr ) )
	{	
		SAFE_RELEASE(pVSBlob);
		return;
	}

	hr = CompileShaderFromFile( L"SimpleSample.fx", "RenderSceneAlphaVS", "vs_4_0", &pVSBlob );
	if( FAILED( hr ) )
	{
		MessageBox( NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
		return;
	}

	// Create the vertex shader
	hr = pd3dDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pBlockAlphaVertexShader );
	if( FAILED( hr ) )
	{	
		SAFE_RELEASE(pVSBlob);
		return;
	}

	// Create the input layout
	hr = pd3dDevice->CreateInputLayout( BlockLayout, ARRAYSIZE( BlockLayout ), pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), &m_pLayout11 );
	pVSBlob->Release();
	if( FAILED( hr ) )
		return;

	hr = CompileShaderFromFile( L"SimpleSample.fx", "RenderScenePS", "ps_4_0", &pVSBlob );
	if( FAILED( hr ) )
	{
		MessageBox( NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
		return;
	}

	// Create the vertex shader
	hr = pd3dDevice->CreatePixelShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pBlockPixelShader );
	if( FAILED( hr ) )
	{	
		SAFE_RELEASE(pVSBlob);
		return;
	}


	hr = CompileShaderFromFile( L"SimpleSample.fx", "RenderSceneAlphaPS", "ps_4_0", &pVSBlob );
	if( FAILED( hr ) )
	{
		MessageBox( NULL,
			L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
		return;
	}

	// Create the vertex shader
	hr = pd3dDevice->CreatePixelShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pBlockAlphaPixelShader );
	if( FAILED( hr ) )
	{	
		SAFE_RELEASE(pVSBlob);
		return;
	}

	m_pDebugText		= new CDXUTTextHelper( pd3dDevice, pd3dImmediateContext, m_DialogResourceManager, 15 );
	m_pd3dDevice		= pd3dDevice;

#ifdef DEFERRED_UPDATE
	pd3dDevice->CreateDeferredContext( 0, &m_pDeferredContext );
#else
	m_pDeferredContext = pd3dImmediateContext;
#endif

	m_pLoader->setStatus( 0 );
	ResumeThread( m_pLoader->m_Thread_Handle );
	ResumeThread( m_pTickUpdater->m_Thread_Handle );
}

void RenderableWorld::OnD3D11DestroyDevice( void* pUserContext ) {
	SuspendThread( m_pTickUpdater->m_Thread_Handle );
	SuspendThread( m_pLoader->m_Thread_Handle );

	if ( m_pd3dDevice == NULL ) return;

	for (int i = 0; i < map->size(); i++) {
		Region * regio = map->getValue(i);
		if ( regio )
			regio->Release();
	}
	m_Sky->OnD3D11DestroyDevice();

	for (auto it = m_pEntityItems.begin(); it != m_pEntityItems.end(); it++) {
		(*it)->OnD3D11DestroyDevice( );
	}
	for (auto it = m_pEntityLivings.begin(); it != m_pEntityLivings.end(); it++) {
		(*it)->OnD3D11DestroyDevice( );
	}
	SAFE_RELEASE( m_pDeferredContext );

	SAFE_RELEASE( m_pVertexBuffer );
	SAFE_RELEASE( m_pIndexBuffer );
	SAFE_RELEASE( m_pVertexLayout );
	SAFE_RELEASE( m_pVertexShader );
	SAFE_RELEASE( m_pPixelShader );

	SAFE_RELEASE( m_pEntityVertexShader );
	SAFE_RELEASE( m_pBlockVertexShader );
	SAFE_RELEASE( m_pBlockPixelShader );
	SAFE_RELEASE( m_pBlockAlphaVertexShader );
	SAFE_RELEASE( m_pBlockAlphaPixelShader );

	SAFE_RELEASE( m_pcbVSPerFrame11 );
	SAFE_RELEASE( m_pcbPSPerFrame11 );
	SAFE_RELEASE( m_pcbVSPerObject11 );


	SAFE_RELEASE( m_pSamplerStateBlock11 );
	SAFE_RELEASE( m_pBlendState11 );

	SAFE_RELEASE( m_pTextureRV );
	SAFE_RELEASE( m_pTextureAlphaRV );

	SAFE_RELEASE( m_pLayout11 );

	SAFE_DELETE( m_pDebugText );

	indexSize = 0;
#ifdef DEFERRED_UPDATE
	SAFE_RELEASE( m_pDeferredContext );
#else
	m_pDeferredContext = NULL;
#endif

	m_pd3dDevice = NULL;
}

void RenderableWorld::OnD3D11ResizedSwapChain( ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ) {
	//m_DialogResourceManager->OnD3D11ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc );
}
void RenderableWorld::OnD3D11ReleasingSwapChain( void* pUserContext ) {
	//	m_DialogResourceManager->OnD3D11ReleasingSwapChain( );
}

void RenderableWorld::handleMouse( bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown, bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta, int xPos, int yPos, void* pUserContext ) {
	if (nMouseWheelDelta != 0)
		m_Inventory->setSelected(-nMouseWheelDelta / 100);
	m_InventoryHUD.handleMouse( bLeftButtonDown, bRightButtonDown, bMiddleButtonDown, bSideButton1Down, bSideButton2Down, nMouseWheelDelta, xPos, yPos);

	if (bLeftButtonDown && !m_bLeftButtonDown) {
		D3DXVECTOR3 vec;
		Block block;
		//remove
		if (ray(&m_EyePt, &m_Direction, &vec, &block)) {
			int x = static_cast<int>(vec.x);
			int z = static_cast<int>(vec.z);
			int y = static_cast<int>(vec.y);

			block = getBlock(x, z, y);

			breakBlock(x, z, y);

			D3DXVECTOR3 dir = D3DXVECTOR3((rand() % 4000 / 1000.0f) - 2.0f, (rand() % 1000 / 1000.0f) + 1.0f, (rand() % 4000 / 1000.0f) - 2.0f);
			dir *= 4.0f;
			EntityItem * ent = new EntityItem(new ItemBlock(block, 1), vec, dir);
			ent->OnD3D11CreateDevice( m_pd3dDevice );
			m_pEntityItems.push_back(ent);
		}
	}
	if (bRightButtonDown && !m_bRightButtonDown) {
		D3DXVECTOR3 vec;
		Block block;
		Byte facing = 0;
		// SET
		if (ray(&m_EyePt, &m_Direction, &vec, &block, true, &facing)) {
			Item * item = m_Inventory->getSelected();
			if (item)
			if (item->getID() < 256) {
				ItemBlock * itemblock = (ItemBlock*) item;
				block = itemblock->getBlock();

				if (!m_freefly) {
					(*item) -= 1;
					if (item->getQuantity() <= 0)
						m_Inventory->removeSelected();
				}

				int x = static_cast<int>(vec.x);
				int z = static_cast<int>(vec.z);
				int y = static_cast<int>(vec.y);

				/* torch */
				if ((block.blockID == 40) || (block.blockID == 91))
					block.metadata |= facing;
				/* torch */

				placeBlock(x, z, y, block);
			}
		}
	}
	// Water
	if (bMiddleButtonDown && !m_bMiddleButtonDown) {
		D3DXVECTOR3 vec;
		Block block;
		if (ray(&m_EyePt, &m_Direction, &vec, &block, true)) {
			block = STILLWATER_BLOCK;

			int x = static_cast<int>(vec.x);
			int z = static_cast<int>(vec.z);
			int y = static_cast<int>(vec.y);

			placeBlock(x, z, y, block);
		}
	}
	//redstone
	if (bSideButton1Down && !m_bSideButton1Down) {
		D3DXVECTOR3 vec;
		Block block;
		if (ray(&m_EyePt, &m_Direction, &vec, &block, true)) {
			block = REDSTONE_DUST;
			//block.metaLo = 15;
			int x = static_cast<int>(vec.x);
			int z = static_cast<int>(vec.z);
			int y = static_cast<int>(vec.y);

			placeBlock(x, z, y, block);
		}
	}

	m_bLeftButtonDown = bLeftButtonDown;
	m_bRightButtonDown = bRightButtonDown;
	m_bMiddleButtonDown = bMiddleButtonDown;
	m_bSideButton1Down = bSideButton1Down;
	m_bSideButton2Down = bSideButton2Down;
};

void RenderableWorld::handleKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext ) {
//	m_bAlt = bAltDown;

	if( bKeyDown )
	{
		switch( nChar )
		{
			/* http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx */
		case 0x42: // B
			{
				D3DXVECTOR3 pos = m_EyePt;//D3DXVECTOR3( 0.0f, 100.0f, 0.0f);
				D3DXVECTOR3 dir = m_pPlayer->getViewDirection() * 20.0f; dir.x *= 3.0f; dir.z *= 3.0f;
				Item * item = m_Inventory->getSelected();
				EntityItem * ent = new EntityItem(item->clone(), pos, dir);
				ent->OnD3D11CreateDevice( m_pd3dDevice );
				m_pEntityItems.push_back(ent);

				if (!m_freefly) {
					(*item) -= 1;
					if (item->getQuantity() <= 0)
						m_Inventory->removeSelected();
				}
			}
			break;
		case 0x43: // C
			{
				D3DXVECTOR3 pos = m_pPlayer->getPosition();//D3DXVECTOR3( 0.0f, 100.0f, 0.0f);
				EntityLiving * ent = new EntityCactusMan(pos);
				ent->OnD3D11CreateDevice( m_pd3dDevice );
				m_pEntityLivings.push_back(ent);
			}
			break;
		case 0x46: // F
			m_freefly  = !m_freefly;
			if (m_freefly) {
				m_Camera->SetScalers(0.01f, 50.0f);
			} else {
				m_Camera->SetScalers(0.01f, 0.0f);
			}
			break;
		case 0x31: // 1
			m_pPlayer->inflictStatus(1, 10.0f);
			break;
		case 0x32: // 2
			m_pPlayer->inflictStatus(2, 10.0f);
			break;
		case 0x33: // 3
			m_pPlayer->inflictStatus(3, 10.0f);
			break;
		case 0x34: // 4
			m_pPlayer->inflictStatus(-1, 10.0f);
			break;
		case 0x4A: // J
			m_pPlayer->setJuced(120.0f);
			break;
		case 0x6D: // -
			m_Daytime -= m_Daylength / 12.0f;
			break;
		case 0x6B: // +
			m_Daytime += m_Daylength / 12.0f;
			break;
		case VK_F1: // F1
			{
				WCHAR strCapName[MAX_PATH];
				UINT index = 0;
				do {
					swprintf_s( strCapName, L"Savegames/screenshot%d.jpg", index );
					index++;
				} while (exists(strCapName));
				DXUTSnapD3D11Screenshot( strCapName, D3DX11_IFF_JPG );
				wprintf(L"%s saved ... \n", strCapName);
			}
			break;
		case VK_F3: // F3
			m_showDebug = !m_showDebug;
			break;
		}
	}
};

bool RenderableWorld::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
	//m_LifeHUD.MsgProc( hWnd, uMsg, wParam, lParam );

    switch( uMsg )
    {
        case WM_KEYDOWN:
			{
				//UINT Key = ( ( UINT )wParam );
				switch (wParam)
				{
				case 0x57: //  W key
					m_bWKey = true;
					break;
				case 0x41: //  A key
					m_bAKey = true;
					break;
				case 0x53: //  S key
					m_bSKey = true;
					break;
				case 0x44: //  D key
					m_bDKey = true;
					break;
				case 0x20: //  Space Key
					m_bJump = true;
					break;
				case 0x10: // LEFT Shift
					m_bShift = true;
					break;
				case 0x11: // Ctrl

					break;
				default:
					break;
				}
			}
			break;
		case WM_KEYUP:
			{
				//UINT Key = ( ( UINT )wParam );
				switch (wParam)
				{
				case 0x57: //  W key
					m_bWKey = false;
					break;
				case 0x41: //  A key
					m_bAKey = false;
					break;
				case 0x53: //  S key
					m_bSKey = false;
					break;
				case 0x44: //  D key
					m_bDKey = false;
					break;
				case 0x20: //  Space Key
					m_bJump = false;
					break;
				case 0x10: // LEFT Shift
					m_bShift = false;
					break;
				case 0x11: // Ctrl

					break;
				default:
					break;
				}
			}
			break;
	}
	return TRUE;
};

void RenderableWorld::drawDebugText() {
	WCHAR	   str[MAX_PATH];
	INTVECTOR3 pos	= INTVECTOR3(m_EyePt);
	Block block		= getBlock(pos.x, pos.z, pos.y);
	int x			= pos.x;
	int z			= pos.z;

	m_pDebugText->Begin();
	m_pDebugText->SetInsertionPos( 5, 5 );
	m_pDebugText->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
	m_pDebugText->DrawTextLine( DXUTGetFrameStats( DXUTIsVsyncEnabled() ) );
	m_pDebugText->DrawTextLine( DXUTGetDeviceStats() );
	swprintf_s( str, L"X:%6.2f Y:%6.2f Z:%6.2f", m_EyePt.x, m_EyePt.y, m_EyePt.z);
	m_pDebugText->DrawTextLine( str );
	swprintf_s( str, L"Biome:%s T:%4.2f H:%4.2f", BiomeToStr(getBiome( x, z )), getTemperature( x, z ), getHumidity( x, z ));
	m_pDebugText->DrawTextLine( str );
	D3DXVECTOR3 vec;
	Byte facing = 0;
	Block blockbuf;
	if (ray(&m_EyePt, &m_Direction, &vec, &blockbuf, false, &facing)) {
		pos				= INTVECTOR3(vec);
		blockbuf		= getBlock(pos.x, pos.z, pos.y);
		swprintf_s( str, L"BL:0x%X SL:0x%X BM:0x%X - BID:%d BS:0x%X BM:0x%X BL:0x%X SL:0x%X", block.blockLight, block.skyLight, block.metadata, blockbuf.blockID, blockbuf.status, blockbuf.metadata, blockbuf.blockLight, blockbuf.skyLight);
	} else {
		swprintf_s( str, L"BL:0x%X SL:0x%X BM:0x%X", block.blockLight, block.skyLight, block.metadata);
	}
	m_pDebugText->DrawTextLine( str );
	swprintf_s( str, L"E:%d (EL:%d EI:%d)", m_pEntityItems.size() + m_pEntityLivings.size(), m_pEntityLivings.size(), m_pEntityItems.size());
	m_pDebugText->DrawTextLine( str );
	m_pDebugText->End();
};

D3DXVECTOR3 RenderableWorld::getPlayerPosition(int index) {
	return m_EyePt;
};

Block RenderableWorld::breakBlock(int x, int z, int y) {
	Block block = getBlock(x, z, y);
	switch (block.blockID)
	{
	case 1:
		m_pSound->Quickplay(BLOCK_BREAK_SOUND_DIRT);
		break;
	case 2:
		m_pSound->Quickplay(BLOCK_BREAK_SOUND_GRASS);
		break;
	case 3:
		m_pSound->Quickplay(BLOCK_BREAK_SOUND_STONE);
		break;
	case 14:
		m_pSound->Quickplay(BLOCK_BREAK_SOUND_GRASS);
		break;
	case 20:
		m_pSound->Quickplay(BLOCK_BREAK_SOUND_SAND);
		break;
	case 10:
		m_pSound->Quickplay(BLOCK_BREAK_SOUND_WOOD);
		break;
	default:
		break;
	}
	World::breakBlock(x, z, y);
	return block;
}

/* ---- MULTIPLAYER ---- */

ClientWorld::ClientWorld(CFirstPersonCamera * cam, HUDDialogResourceManager* manager) {
	Dirty				= false;

	m_Daytime			= 0.0f;
	m_Daylength			= 50.0f;

	m_pd3dDevice		= NULL;
	m_pDebugText		= NULL;
#ifdef DEFERRED_UPDATE
	InitializeSRWLock(&DEFERRED_LOCK);
#endif

	m_bLeftButtonDown	= false;
	m_bRightButtonDown  = false;
	m_bMiddleButtonDown = false;
	m_bSideButton1Down	= false;
	m_bSideButton2Down	= false;

	m_freefly			= true;
	m_showDebug			= true;
	m_SafeFiles			= false;

	/* DEBUG */
	printf ("Beginning generation ...\n");

	m_Camera	= cam;
	m_Camera->SetScalers(0.01f, 50.0f);

	m_EyePt		= *m_Camera->GetEyePt();

	/* Load Region */
	map				= new ClientRegionmap(this, false);

	/* Threads last */
	m_pLoader		= new Loader(this);
	m_pTickUpdater	= new TickUpdater(this);

	/*  Geometry */
	m_Sky		= new Skybox();

	/* UI */
	m_DialogResourceManager = manager;
	m_Inventory = new Inventory(10, 10, m_DialogResourceManager);

	m_pConnection = new ClientMainThread(this);
};

ClientWorld::~ClientWorld() {
	m_pConnection->Terminate();
};

int ClientWorld::loadRegion(int x, int z) {
//	BYTE ix = abs(x - ((x >> 4) << 4));
//	BYTE iz = abs(z - ((z >> 4) << 4));

	Region * regio = createRegion(x, z);
	if (!regio) {
		printf("Failed to create Region!\n");
		return -1;
	}
	/* Get Chunk */
	int mem[] = {x, z};
	if(m_pConnection->Send(Protostruct(100), (BYTE*)mem, 8, true))
		return 0;
	else 
		return -1;
}

int ClientWorld::setCompressedCluster(int x, int z, byte *  memory, uLong   size) {
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));
	Region * regio = getRegion(x, z);

	if (regio) {
		int result = regio->setCompressedCluster(ix, iz, memory, size);
		//generateVB(x, z); 
		return result;
	} else {
		return 0;
	}
};


int ClientWorld::placeBlock(int x, int z, int y, Block block) {
	BYTE * mem = (BYTE*)malloc(sizeof(Block) + sizeof(INTVECTOR3));
	INTVECTOR3 pos = INTVECTOR3(x, z, y);
	memcpy(mem, &pos, sizeof(INTVECTOR3));
	memcpy(mem + sizeof(INTVECTOR3), &block, sizeof(Block));
	m_pConnection->Send(Protostruct(200), (BYTE*)mem, sizeof(Block) + sizeof(INTVECTOR3)); 

	setBlock(x, z, y, block);
	delayedUpdateBlock(x, z, y, BLOCK_PLACED, 0);
	renewDirty(true);
	return 1;
}

Block ClientWorld::breakBlock(int x, int z, int y) {
	Region * regio = getRegion(x >> 4, z >> 4);
	if (regio) {
		BYTE * mem = (BYTE*)malloc(sizeof(INTVECTOR3));
		INTVECTOR3 pos = INTVECTOR3(x, z, y);
		memcpy(mem, &pos, sizeof(INTVECTOR3));
		m_pConnection->Send(Protostruct(202), (BYTE*)mem, sizeof(INTVECTOR3)); 

		delayedUpdateBlock(x, z, y, BLOCK_REMOVED, 0);
		Block block = regio->breakBlock(x, z, y);
		delayedUpdateBlock(x, z, y, BLOCK_PLACED, 0);
		renewDirty(true);
		return block;
	} else {
		return VOID_BLOCK;
	}
}

#endif /* CLIENT */

#ifdef SERVER
ServerWorld::ServerWorld() : World() {
	/* Load Savefile*/
	CreateDirectory(L"Savegames", NULL); 

	int file_exists;
	char filename[] = "Savegames/Savefile.sv";

	fopen_s(&m_file, filename,"r");
	if (m_file == NULL) 
		file_exists = 0;
	else {
		file_exists = 1; 
		fclose(m_file);
	}

	if (file_exists == 1)
	{
		printf("%s opened!\n", filename);
		fopen_s(&m_file, filename,"r+b");
		//fread(&m_EyePt, sizeof(D3DXVECTOR3), 1, m_file);
		//D3DXVECTOR3 lookat = *cam->GetLookAtPt();
		//cam->SetViewParams(&m_EyePt, &lookat);
		fread(&m_Daytime, sizeof(double), 1, m_file);
		fread(&m_Seed, sizeof(int), 1, m_file);
	}
	else
	{
		printf("%s created!\n", filename);
		fopen_s(&m_file, filename,"w+b");
		//fwrite(&m_EyePt, sizeof(D3DXVECTOR3), 1, m_file);
		fwrite(&m_Daytime, sizeof(double), 1, m_file);

		srand(time(NULL));
		m_Seed = rand();
		fwrite(&m_Seed, sizeof(int), 1, m_file);
	}

	m_pLoader		= new Loader(this);
	ResumeThread( m_pLoader->m_Thread_Handle );
	m_pConnection	= new ServerMainThread(this);
};

ServerWorld::~ServerWorld() {
	for (auto it = m_Player.begin(); it != m_Player.end(); it++) {
		Loader * pLoader = it->second.pLoader;
		//ResumeThread( pLoader->m_Thread_Handle );
		pLoader->Terminate();
	}
	m_pLoader->Terminate();
	m_pConnection->Terminate();
};

void ServerWorld::connectPlayer(int ID) {
	PlayerData player = {new Loader(this, ID)};
	ResumeThread( player.pLoader->m_Thread_Handle );
	m_Player.insert(pair<int,PlayerData>(ID,player));
};

void ServerWorld::disconnectPlayer(int ID) {
	auto it = m_Player.find(ID);
	if (it != m_Player.end()) {
		Loader * pLoader = it->second.pLoader;
		pLoader->Terminate();
		m_Player.erase(it);
	}
};

int ServerWorld::setCompressedCluster(int x, int z, byte *  memory, uLong   size) {
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));
	Region * regio = getRegion(x, z);

	if (regio) {
		return regio->setCompressedCluster(ix, iz, memory, size);
	} else {
		return 0;
	}
};

int ServerWorld::getCompressedCluster(int x, int z, byte ** memory, uLong * size) {
	int ix = abs(x - ((x >> 4) << 4));
	int iz = abs(z - ((z >> 4) << 4));
	Region * regio = getRegion(x, z);

	if (regio) {
		return regio->getCompressedCluster(ix, iz, memory, size);
	} else {
		if (loadRegion(x, z) > 0) {
			regio = getRegion(x, z);
			return regio->getCompressedCluster(ix, iz, memory, size);
		} else {
			return 0;
		}
	}
};

void ServerWorld::setPlayerPosition(D3DXVECTOR3 pos, int ID) {
	auto it = m_Player.find(ID);
	if (it != m_Player.end()) {
		it->second.position = pos;
	}
};

D3DXVECTOR3 ServerWorld::getPlayerPosition(int ID) {
	auto it = m_Player.find(ID);
	if (it != m_Player.end()) {
		INT64 time = GetTickCount64();
		if (it->second.lastUpdate + 100 < time) {
			it->second.lastUpdate = time;
			m_pConnection->Send(ID, Protostruct(50), (BYTE*)NULL, 0);
		}
		return it->second.position;
	} else {
		return D3DXVECTOR3();
	}
};

#endif /* SERVER */

//--------------------------------------------------------------------------------------
// Handles the GUI events
//--------------------------------------------------------------------------------------
void CALLBACK bOnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
	switch( nControlID )
	{
	case 4: 
		printf( "Click\n");
		break;
	}
}